.data
    result:     .space  256  #a int_256, can be seen as int_32[64] since a int_32 has 4 bytes, 
    startmsg:   .asciiz "Input the factorial argument: "
    donemsg:    .asciiz "The result is: "

.text

start:
    li      $v0, 4                  #print string
    la      $a0, startmsg          
    syscall              
    li      $v0, 5                  #read integer
    syscall
    move    $s2, $v0                #s2 is argument, also multiplier each time
    li      $s1, 1                  #s1 is constant 1
    li      $s3, 64                 #s3 is result's length
    li      $s4, 10000000           #s4 is constant representing base, must be 10's power, should be less than half of 2,147,483,647
    la      $s0, result             #s0 is result[0] address
    sw      $s1, 0($s0)             #Initial result's value as 1

fact:
    li      $t0, 0                  #t0 as a index of array `result`, start from lowest pos (0 ~ 64)
    move    $t3, $zero              #t3 is a overflow symbol
    move    $t5, $zero              #t5 is the number needs to carry in
    move    $s6, $zero
    mulLoop:
        move    $t1, $t0                #t1 <- index
        sll     $t1, $t1, 2             #t1 shift left twice: t1 *= 4 
        add     $t1, $t1, $s0           #get address of result[index]
        lw      $t2, 0($t1)             #t2 is a temp number
        move    $t3, $zero              #set overflow symbol to 0
        move    $t4, $t2                #t4 is temp result
        beq     $s5, $s1, onlyAdd       #if s5 = constant 1, simply Add
        beq     $t4, $zero, isEND       #if t4=0, end this time
        
        mulu    $t4, $s2, $t4
    onlyAdd:
        add     $t4, $t4, $t5
        sltu    $t3, $s4, $t4           #if t4>100000, overflow happened, set t3 to 1, need to cin
        beq     $t3, $s1, isOF          #jumps to isOF process
        move    $s5, $zero
        move    $t5, $zero
        j       isEND
    isOF:
        move    $t6, $t0
        add     $t6, $t6, $s1           #t6 <- index + 1
        beq     $t6, $s3, isEND         #reach last unit of the array, terminate

        sll     $t6, $t6, 2             
        add     $t6, $t6, $s0           #t6 <- address of result[index+1]
        lw      $s7, 0($t6)
        sltu    $s6, $zero, $s7         #if result[index + 1] > 0,  s6 = 1
        divu    $t4, $s4
        mflo    $t5                     #t5 = quotient
        mfhi    $t4                     #t4 = remainder, result[index] = t4

    isEND:
        sw      $t4, 0($t1)
        addi    $t0, $t0, 1             #index++
        sltu    $t8, $s6, $s1           #s6 < 1: 
        sltu    $t9, $zero, $t3         #t3 > 0: carry happend
        and     $s5, $t8, $t9           #set s5 to 1: carry finished, simply add next loop
        beq     $t0, $s3, endloop       #reach last unit of the array, terminate
        j       mulLoop

    loopEnd:
        sub     $s2, $s2, $s1           #multiplier--
        beq     $s2, $s1, done          #if multiplier=1, calculation finished
        j       fact                    #else continue multiplying

done:
    la      $a0, donemsg
    li      $v0, 4
    syscall
    move    $t0, $s3
    sub     $t0, $t0, $s1
    li      $t4, -1
    move    $t1, $zero
    move    $t2, $zero
printloop:
	move    $t1, $t0
    sll     $t1, $t1, 2
    add     $t1, $t1, $s0
    lw      $t2, 0($t1)
    move    $a0, $t2
    li      $v0, 36                 #print integer as unsigned
    syscall
    sub     $t0, $t0, $s1
    beq     $t0, $t4, end
    j       printloop
end:
    li      $v0, 10                 #exit (terminate execution)
    syscall
